package com.gitee.apanlh.util.net.http.handler.body;

import com.gitee.apanlh.exp.HttpBodyParseException;
import com.gitee.apanlh.util.base.IteratorUtils;
import com.gitee.apanlh.util.base.StringUtils;
import com.gitee.apanlh.util.encode.StrEncodeUtils;
import com.gitee.apanlh.util.io.FileIOUtils;
import com.gitee.apanlh.util.io.IOUtils;
import com.gitee.apanlh.util.net.http.HttpBody;
import com.gitee.apanlh.util.net.http.HttpClient;
import com.gitee.apanlh.util.net.http.HttpRequest;
import com.gitee.apanlh.util.net.http.model.FormDataFileResource;
import com.gitee.apanlh.util.reflection.ClassConvertUtils;
import com.gitee.apanlh.util.reflection.ClassTypeUtils;
import com.gitee.apanlh.util.unit.BuffSize;
import com.gitee.apanlh.web.http.HttpContentType;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.util.Map;

/**
 * 	HTTPBody请求体解析
 * 	<br>multipart/form-data表单请求
 * 	
 * 	@author Pan
 */
public class HttpBodyFormDataParseStrategy implements HttpBodyParseStrategy {
	
	private static final String DISPOSITION = "Content-Disposition: form-data; name=\"";
	private static final String CRLF = "\r\n";
	private static final byte[] CRLF_BYTES = CRLF.getBytes();
	private static final String HEADER_PART = "\r\n\r\n";
	/** 文件名称 */
	private static final String FILE_NAME = "\"; filename=\"";

	@Override
	public HttpRequest parse(HttpRequest httpRequest, HttpClient httpClient) throws HttpBodyParseException {
		return toFormData(httpRequest);
	}
	
	 /**	
     * 	将对象转化请求方式为NameValuePair格式(值对节点类型)
     * 	<br>顺序添加节点
     * 	
     * 	@author Pan
     * 	@param 	httpRequest	请求对象
     * 	@return	HttpRequest
     */
	@SuppressWarnings("unchecked")
	HttpRequest toFormData(HttpRequest httpRequest) {
		Map<String, ?> paramMap;
		HttpBody httpBody = httpRequest.getBody();
		Object requestBody = httpBody.getRequestBody();
		String formDataBoundary = httpBody.getFormDataBoundary();
		String charset = httpRequest.getConfig().getCharset();
		boolean hasParamUrlEncode = httpRequest.getConfig().hasParamUrlEncode();
		StringBuilder sb = StringUtils.createBuilder();
		
		if (httpBody.isRequestBodyMap()) {
			paramMap = (Map<String, ?>) requestBody;
		} else {
			paramMap = ClassConvertUtils.toLinkedHashMap(requestBody);
		}
		
		ByteArrayOutputStream baos = new ByteArrayOutputStream(BuffSize.SIZE_8K);


		IteratorUtils.entrySet(paramMap, (k, v) -> {
			sb.append("--").append(formDataBoundary).append(CRLF);
			
			if (ClassTypeUtils.isString(v)) {
				sb.append(DISPOSITION).append(k).append("\"");
				sb.append(CRLF);
				sb.append(HttpContentType.appendContentType(HttpContentType.TEXT_PLAIN, "charset=")).append(charset);
				sb.append(HEADER_PART);
				String value = String.valueOf(v);
				if (hasParamUrlEncode) {
					sb.append(StrEncodeUtils.urlEncode(value));
				} else {
					sb.append(value);
				}
				sb.append(CRLF);
				IOUtils.write(sb.toString().getBytes(), baos);
			}
			if (ClassTypeUtils.isArrayByte(v)) {
			    sb.append(DISPOSITION).append(k).append(FILE_NAME).append(k).append("\"");
			    sb.append(CRLF);
			    sb.append(HttpContentType.appendContentType(HttpContentType.APPLICATION_OCTET_STREAM));
		        sb.append(HEADER_PART);
		        IOUtils.write(sb.toString().getBytes(), baos);
		        IOUtils.write((byte[]) v, baos);
		        IOUtils.write(CRLF_BYTES, baos);
			}
			if (ClassTypeUtils.isFile(v)) {
				File file = (File) v;
				byte[] read = FileIOUtils.read(file);
				sb.append(DISPOSITION).append(k).append(FILE_NAME).append(file.getName()).append("\"");
			    sb.append(CRLF);
			    sb.append(HttpContentType.appendContentType(HttpContentType.APPLICATION_OCTET_STREAM));
		        sb.append(HEADER_PART);
		        IOUtils.write(sb.toString().getBytes(), baos);
		        IOUtils.write(read, baos);
		        IOUtils.write(CRLF_BYTES, baos);
			}
			if (ClassTypeUtils.isInputStream(v)) {
			    sb.append(DISPOSITION).append(k).append(FILE_NAME).append(k).append("\"");
			    sb.append(CRLF);
			    sb.append(HttpContentType.appendContentType(HttpContentType.APPLICATION_OCTET_STREAM));
		        sb.append(HEADER_PART);
		        IOUtils.write(sb.toString().getBytes(), baos);
		        IOUtils.write(IOUtils.read((InputStream) v), baos);
		        IOUtils.write(CRLF_BYTES, baos);
			}
			if (v instanceof FormDataFileResource) {
				appendFormDataFileResource(sb, baos, k, v);
			}
			sb.setLength(0);
		});
		
		sb.append("--").append(formDataBoundary).append("--").append(CRLF);
		IOUtils.write(sb.toString().getBytes(), baos);
		
		httpBody.setRequestBody(baos.toByteArray());
		httpRequest.setBody(httpBody);
		return httpRequest;
	}
	
	/**	
	 * 	解析并添加资源对象FormData格式
	 * 	
	 * 	@author Pan
	 * 	@param 	sb		原有字符串
	 * 	@param 	baos	字节输出流
	 * 	@param 	k		键
	 * 	@param 	v		值
	 */
	private void appendFormDataFileResource(StringBuilder sb, ByteArrayOutputStream baos, String k, Object v) {
		FormDataFileResource formDataFileResource = (FormDataFileResource) v;
		sb.append(DISPOSITION).append(k).append(FILE_NAME).append(formDataFileResource.getOriginalFilename()).append("\"");
	    sb.append(CRLF);
	    sb.append(HttpContentType.appendContentType(HttpContentType.APPLICATION_OCTET_STREAM));
        sb.append(HEADER_PART);
        IOUtils.write(sb.toString().getBytes(), baos);
        IOUtils.write(((FormDataFileResource) v).getBytes(), baos);
        IOUtils.write(CRLF_BYTES, baos);
	}
}
